package com.appspot.themaopdracht5.server.authorization;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;



import com.appspot.themaopdracht5.shared.UserRoleType;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

/**
 * 
 * @author alex.jongman
 * This class is meant to run on the Google App Engine and 
 * requires the Google Datastore.
 * This class is also using the class AuthorizedUser and 
 * an enumeration class called UserRoleType.
 * 
 */
public class Authorization {

	private static final Logger log = Logger.getLogger(Authorization.class.getName());

	private UserService userService;
	private ArrayList<AuthorizedUser> users = new ArrayList<AuthorizedUser>();

	/**
	 * Initializes by fetching all the persistent WoongemakUsers and put them in
	 * the users arraylist.
	 */
	public Authorization() {
		userService = UserServiceFactory.getUserService();

		PersistenceManager pmRead = PMF.get().getPersistenceManager();
		String query = "select from " + AuthorizedUser.class.getName();
		List<AuthorizedUser> authorizedUsers = (List<AuthorizedUser>) pmRead.newQuery(query).execute();

		for (AuthorizedUser authorizedUser : authorizedUsers) {
			users.add(authorizedUser);
		}
		pmRead.close();
	}

	/**
	 * Determines whether or not the current user is authorized; note that the
	 * real admin always is authorized
	 * 
	 * @param accessControlList
	 *            a list of UserRoleTypes that are authorized.
	 * @param email
	 *            used in case that you want a certain user also to be
	 *            authorized
	 * @return returns true if the user is authorized otherwise false will be
	 *         returned
	 */
	public boolean isAuthorized(ArrayList<UserRoleType> accessControlList, String email) {
		boolean authorized = false;

		ArrayList<UserRoleType> userGroupRights = getRights();

		for (UserRoleType urt : userGroupRights) {
			authorized = authorized || accessControlList.contains(urt);
		}

		if (userService.isUserLoggedIn() && (email != null)) {
			email = email.toLowerCase().trim();
			authorized = authorized || (userService.getCurrentUser().getEmail().toLowerCase().equals(email.toLowerCase()));
		}

		authorized = authorized || userService.isUserAdmin();

		log.info("Authorization request for " + accessControlList + " was " + authorized);

		return authorized;
	}

	/**
	 * Adds a user to the system and assigns the user the rights belonging to
	 * the list of groups the user is associated with
	 * 
	 * @param email
	 *            the email address is used as a unique identifier of the user
	 * @param rights
	 *            the groups the user belongs to
	 * @return true when the user is added otherwise false
	 */
	public boolean addUser(String email, ArrayList<UserRoleType> rights) {
		boolean result = false;
		email = email.toLowerCase().trim();
		if (getUser(email) == null) {
			AuthorizedUser wgu = new AuthorizedUser(email, rights);
			PersistenceManager pmWrite = PMF.get().getPersistenceManager();
			try {
				pmWrite.makePersistent(wgu);
			} finally {
				pmWrite.close();
			}
			result = users.add(wgu);
		}

		return result;
	}

	/**
	 * Removes a user from the system
	 * 
	 * @param email
	 *            The email address of the user to be removed
	 * @return true when the user has been removed, otherwise false will be
	 *         returned
	 */
	public boolean removeUser(String email) {
		boolean result = false;
		email = email.toLowerCase().trim();
		AuthorizedUser user = getUser(email);
		if (user != null) {
			PersistenceManager pmRemove = PMF.get().getPersistenceManager();
			AuthorizedUser woongemakUser = pmRemove.getObjectById(AuthorizedUser.class, user.getEmail());
			pmRemove.deletePersistent(user);
			result = users.remove(user);
		}

		return result;
	}

	/**
	 * Adds a right to a given user.
	 * 
	 * @param email
	 *            the email address that identifies the user who shall get the
	 *            right
	 * @param right
	 *            the right (a.k.a. role) to be added
	 * @return true in case the right was indeed added, otherwise false
	 */
	public boolean addRight(String email, UserRoleType right) {
		boolean result = false;
		email = email.toLowerCase().trim();
		AuthorizedUser user = getUser(email);
		if (user != null) {
			result = user.addRight(right);
		}
		return result;
	}

	/**
	 * Removes a right from a given user.
	 * 
	 * @param email
	 *            the email address that identifies the user from whom the right
	 *            shall be removed
	 * @param right
	 *            the right (a.k.a. role) to be removed
	 * @return true in case the right was indeed removed, otherwise false
	 */
	public boolean removeRight(String email, UserRoleType right) {
		boolean result = false;
		email = email.toLowerCase().trim();
		AuthorizedUser user = getUser(email);
		if (user != null) {
			result = user.removeRight(right);
		}
		return result;
	}

	/**
	 * Determine the rights of the current user
	 * 
	 * @return arraylist of UserRoleTypes of the current user.
	 */
	public ArrayList<UserRoleType> getRights() {
		ArrayList<UserRoleType> result = new ArrayList<UserRoleType>();

		if (this.userService.isUserLoggedIn()) {
			String email = this.userService.getCurrentUser().getEmail();
			AuthorizedUser authorizedUser = getUser(email);
			if (authorizedUser != null) {
				result = authorizedUser.getBelongsTo();
			}
		}

		return result;
	}

	/**
	 * Fetches an instance of user, identified by his/her email adress
	 * 
	 * @param the
	 *            email address of the user from which we want the user
	 *            instance
	 * @return the User instance
	 */
	private AuthorizedUser getUser(String email) {
		AuthorizedUser result = null;

		for (AuthorizedUser authorizedUser : users) {
			if (authorizedUser.getEmail().equals(email)) {
				result = authorizedUser;
			}
		}

		return result;
	}

}
